package ch.ti.bfh.app.loodel.internal;

import ch.ti.bfh.app.loodel.domain.learningEvent.LearningEvent;
import ch.ti.bfh.app.loodel.domain.learningEvent.LearningEventRepository;
import ch.ti.bfh.app.loodel.domain.member.Member;
import ch.ti.bfh.app.loodel.domain.timeslot.Location;
import ch.ti.bfh.app.loodel.domain.timeslot.TimeSlot;
import ch.ti.bfh.app.loodel.domain.timeslot.TimeSlotRepository;
import ch.ti.bfh.app.loodel.domain.timeslotvote.TimeSlotVoteRepository;
import ch.ti.bfh.app.loodel.internal.security.InternalUserContext;
import ch.ti.bfh.app.loodel.service.LearningEventInvitationData;
import ch.ti.bfh.app.loodel.service.MemberData;
import ch.ti.bfh.app.loodel.service.SendLearningEventInvitationService;
import ch.ti.bfh.app.loodel.service.admin.LearningEventAdminService;
import ch.ti.bfh.app.loodel.service.admin.LearningEventAdministrationException;
import ch.ti.bfh.app.loodel.service.admin.LearningEventUpdateData;
import ch.ti.bfh.app.loodel.service.admin.TimeSlotUpdateData;
import ch.ti.bfh.app.loodel.service.create.CreateLearningEventData;
import ch.ti.bfh.app.loodel.service.create.CreateLearningEventService;
import ch.ti.bfh.app.loodel.service.create.CreateTimeSlotData;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.Validate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@Transactional
public class DefaultLearningEventAdminService implements LearningEventAdminService {

    private final LearningEventRepository learningEventRepository;

    private final TimeSlotRepository timeSlotRepository;

    private final TimeSlotVoteRepository timeSlotVoteRepository;

    private final InternalUserContext userContext;

    private final SendLearningEventInvitationService sendLearningEventInvitationService;

    private final CreateLearningEventService createLearningEventService;

    @Inject
    public DefaultLearningEventAdminService(LearningEventRepository learningEventRepository, TimeSlotRepository timeSlotRepository, TimeSlotVoteRepository timeSlotVoteRepository, InternalUserContext userContext, SendLearningEventInvitationService sendLearningEventInvitationService, CreateLearningEventService createLearningEventService) {
        this.learningEventRepository = learningEventRepository;
        this.timeSlotRepository = timeSlotRepository;
        this.timeSlotVoteRepository = timeSlotVoteRepository;
        this.userContext = userContext;
        this.sendLearningEventInvitationService = sendLearningEventInvitationService;
        this.createLearningEventService = createLearningEventService;
    }

    @Override
    public void updateLearningEventAndSendUpdateMailNotification(String learningEventId, LearningEventUpdateData learningEventUpdateData, String customUpdateNotificationText) throws LearningEventAdministrationException {
        LearningEvent learningEvent = loadLearningEvent(learningEventId);

        if (learningEventUpdateData.getComment() != null) {
            learningEvent.setDescription(learningEventUpdateData.getComment());
        }

        if (CollectionUtils.isNotEmpty(learningEventUpdateData.getNewTimeSlotListToAdd())) {
            for (CreateTimeSlotData createTimeSlotData : learningEventUpdateData.getNewTimeSlotListToAdd()) {
                TimeSlot timeSlot = new TimeSlot(learningEvent, createTimeSlotData.getDateTimeInterval(), new Location(createTimeSlotData.getLocationName(), createTimeSlotData.getStreet(), createTimeSlotData.getPlace()));
                timeSlotRepository.save(timeSlot);

            }
        }

        Set<TimeSlot> timeSlotUpdate = new HashSet<TimeSlot>();
        if (CollectionUtils.isNotEmpty(learningEventUpdateData.getTimeSlotListToUpdate())) {
            for (TimeSlotUpdateData timeSlotUpdateData : learningEventUpdateData.getTimeSlotListToUpdate()) {
                TimeSlot timeSlotToUpdated = timeSlotRepository.findOne(timeSlotUpdateData.getTimeSlotId());
                // TODO Owner Permission check for the time slot
                timeSlotUpdate.add(timeSlotToUpdated);
                timeSlotToUpdated.setComment(timeSlotUpdateData.getComment());
                timeSlotToUpdated.getLocation().setName(timeSlotUpdateData.getLocationName());
                timeSlotToUpdated.getLocation().setStreet(timeSlotUpdateData.getStreet());
                timeSlotToUpdated.getLocation().setPlace(timeSlotUpdateData.getPlace());
            }
        }

        Set<TimeSlot> timeSlotDelete = new HashSet<TimeSlot>();
        if (CollectionUtils.isNotEmpty(learningEventUpdateData.getTimeSlotIdListToDelete())) {
            for (Long aLong : learningEventUpdateData.getTimeSlotIdListToDelete()) {
                TimeSlot timeSlotToDelete = timeSlotRepository.findOne(aLong);
                // TODO Owner Permission check for the time slot
                timeSlotDelete.add(timeSlotToDelete);
                timeSlotRepository.delete(timeSlotToDelete);
            }
        }

        Set<Member> membersToInform = new HashSet<Member>();
        if (timeSlotUpdate.size() != 0) {
            for (TimeSlot timeSlot : timeSlotUpdate) {
                List<Member> memberList = timeSlotVoteRepository.findAllVotedMemberForTimeSlot(timeSlot.getId());
                membersToInform.addAll(memberList);
            }
        }
        if (timeSlotDelete.size() != 0) {
            for (TimeSlot timeSlot : timeSlotDelete) {
                List<Member> memberList = timeSlotVoteRepository.findAllVotedMemberForTimeSlot(timeSlot.getId());
                membersToInform.addAll(memberList);
            }
        }

        List<String> mailList = new ArrayList<String>();
        for (Member member : membersToInform) {
            mailList.add(member.getEmail());
        }

        LearningEventInvitationData learningEventInvitationData = new LearningEventInvitationData();
        learningEventInvitationData.setEmailList(mailList);
        sendLearningEventInvitationService.sendLearningEventUpdateNofificationMail(learningEvent.getId(), learningEventInvitationData);
    }

    @Override
    public void closeLearningEventVotes(String learningEventId, List<Long> acceptedTimeSlots) throws LearningEventAdministrationException {
        LearningEvent learningEvent = loadLearningEvent(learningEventId);
        Validate.notNull(learningEvent);
        Validate.notEmpty(acceptedTimeSlots);
        learningEvent.rejectAllTimeSlots();
        final Iterable<TimeSlot> timeSlots = timeSlotRepository.findAll(acceptedTimeSlots);
        for (TimeSlot timeSlot : timeSlots) {
            if (!learningEvent.getTimeSlots().contains(timeSlot)) {
                throw new IllegalArgumentException("The provided Time Slots Id doesn't match with the given Learning Event Id");
            }
            timeSlot.setTakingPlace(true);
        }
        learningEvent.closeLearningEventVotes();
    }

    @Override
    public void deleteLearningEvent(String learningEventId) throws LearningEventAdministrationException {
        LearningEvent learningEvent = loadLearningEvent(learningEventId);
        learningEventRepository.delete(learningEvent);
    }


    private LearningEvent loadLearningEvent(String learningEventId) {
        LearningEvent learningEvent = learningEventRepository.findOne(learningEventId);
        Validate.notNull(learningEvent);
        return learningEvent;
    }

    @Override
    public void sendFurtherInvitation(String learningEventId, LearningEventInvitationData learningEventInvitationData) {
        this.sendLearningEventInvitationService.sendLearningEventInvitation(learningEventId, learningEventInvitationData);
    }

    @Override
    public String getDefaultUpdateMailNotificationText(CreateLearningEventData createLearningEventData, String customText) {
        throw new UnsupportedOperationException();
    }

    @Override
    public List<MemberData> findMemberDataForInvitationForExistingLearningEvent(String queryTerm, Long learningEventId) {
        return sendLearningEventInvitationService.findMemberDataForInvitationOfExistingLearningEvent(queryTerm, learningEventId);
    }

}
